package uz.rt.dicom;

import java.util.Arrays;

public class ValueRepresentation implements Comparable<ValueRepresentation>
{
// ------------------------------ FIELDS ------------------------------

    public final static ValueRepresentation ApplicationEntity = new ValueRepresentation("AE", "Application Entity");
    public final static ValueRepresentation AgeString = new ValueRepresentation("AS", "Age String");
    public final static ValueRepresentation AttributeTag = new ValueRepresentation("AT", "Attribute Tag");
    public final static ValueRepresentation CodeString = new ValueRepresentation("CS", "Code String");
    public final static ValueRepresentation Date = new ValueRepresentation("DA", "Date");
    public final static ValueRepresentation DecimalString = new ValueRepresentation("DS", "Decimal String");
    public final static ValueRepresentation DateTime = new ValueRepresentation("DT", "Date Time");

    public final static ValueRepresentation FloatingPointSingle = new ValueRepresentation("FL", "FloatingPointSingle");
    public final static ValueRepresentation FloatingPointDouble = new ValueRepresentation("FD", "FloatingPointDouble");
    public final static ValueRepresentation IntegerString = new ValueRepresentation("IS", "IntegerString");
    public final static ValueRepresentation LongString = new ValueRepresentation("LO", "LongString");
    public final static ValueRepresentation LongText = new ValueRepresentation("LT", "LongText");
    public final static ValueRepresentation OtherByteString = new ValueRepresentation("OB", "OtherByteString");
    public final static ValueRepresentation OtherFloatString = new ValueRepresentation("OF", "OtherFloatString");
    public final static ValueRepresentation OtherWordString = new ValueRepresentation("OW", "OtherWordString");

    public final static ValueRepresentation PersonName = new ValueRepresentation("PN", "Person Name");
    public final static ValueRepresentation ShortString = new ValueRepresentation("SH", "ShortString");
    public final static ValueRepresentation SignedLong = new ValueRepresentation("SL", "SignedLong");
    public final static ValueRepresentation SequenceOfItems = new ValueRepresentation("SQ", "SequenceOfItems");
    public final static ValueRepresentation SignedShort = new ValueRepresentation("SS", "SignedShort");
    public final static ValueRepresentation ShortText = new ValueRepresentation("ST", "ShortText");
    public final static ValueRepresentation Time = new ValueRepresentation("TM", "Time");
    public final static ValueRepresentation UniqueIdentifier = new ValueRepresentation("UI", "Unique Identifier");

    public final static ValueRepresentation UnsignedLong = new ValueRepresentation("UL", "Unsigned Long");
    public final static ValueRepresentation Unknown = new ValueRepresentation("UN", "Unknown");
    public final static ValueRepresentation UnsignedShort = new ValueRepresentation("US", "Unsigned Short");
    public final static ValueRepresentation UnlimitedText = new ValueRepresentation("UT", "Unlimited Text");

    public final static ValueRepresentation NotApplicable = new ValueRepresentation("NA", "Not Applicable");
    public final static ValueRepresentation UnsignedPointer = new ValueRepresentation("UP", "Unsigned Pointer");

    /**
     * A string of characters that identifies an Application Entity with leading and trailing spaces (20H) being
     * non-significant. A value consisting solely of spaces shall not be used.
     */
    public static byte[] AE = {'A', 'E'};
    /**
     * A string of characters with one of the following
     * formats -- nnnD, nnnW, nnnM, nnnY; where
     * nnn shall contain the number of days for D,
     * weeks for W, months for M, or years for Y.
     * Example: “018M” would represent an age of
     * 18 months.
     */
    public static byte[] AS = {'A', 'S'};
    /**
     * Ordered pair of 16-bit unsigned integers that is
     * the value of a Data Element Tag.
     * Example: A Data Element Tag of (0018,00FF)
     * would be encoded as a series of 4 bytes in a
     * Little-Endian Transfer Syntax as
     * 18H,00H,FFH,00H and in a Big-Endian
     * Transfer Syntax as 00H,18H,00H,FFH.
     */
    public static byte[] AT = {'A', 'T'};
    /**
     * A string of characters with leading or trailing
     * spaces (20H) being non-significant.
     */
    public static byte[] CS = {'C', 'S'};
    /**
     * A string of characters of the format
     * YYYYMMDD; where YYYY shall contain year,
     * MM shall contain the month, and DD shall
     * contain the day, interpreted as a date of the
     * Gregorian calendar system.
     * Example:
     * “19930822” would represent August 22, 1993.
     */
    public static byte[] DA = {'D', 'A'};

    /**
     * A string of characters representing either a
     * fixed point number or a floating point number.
     * A fixed point number shall contain only the
     * characters 0-9 with an optional leading "+" or
     * "-" and an optional "." to mark the decimal
     * point. A floating point number shall be
     * conveyed as defined in ANSI X3.9, with an "E"
     * or "e" to indicate the start of the exponent.
     * Decimal Strings may be padded with leading
     * or trailing spaces. Embedded spaces are not
     * allowed.
     * Note: Data Elements with multiple values
     * using this VR may not be properly
     * encoded if Explicit-VR transfer syntax is
     * used and the VL of this attribute
     * exceeds 65534 bytes.
     */
    public static byte[] DS = {'D', 'S'};
    /**
     * <p>A concatenated date-time character string in the format:</p>
     * <p>YYYYMMDDHHMMSS.FFFFFF&ZZXX</p>
     * <p>The components of this string, from left to right, are YYYY = Year, MM = Month, DD =
     * Day, HH = Hour (range "00" - "23"), MM = Minute (range "00" - "59"), SS = Second
     * (range "00" - "60"). FFFFFF = Fractional Second contains a fractional part of a second as small as 1
     * millionth of a second (range “000000” - “999999”).</p>
     * <p>&ZZXX is an optional suffix for offset from Coordinated Universal Time (UTC), where & =
     * “+” or “-”, and ZZ = Hours and XX = Minutes of offset.
     * The year, month, and day shall be interpreted as a date of the Gregorian calendar system.</p>
     * <p>A 24-hour clock is used. Midnight shall be represented by only “0000“ since “2400“ would
     * violate the hour range.</p>
     * <p>The Fractional Second component, if present,
     * shall contain 1 to 6 digits. If Fractional Second is unspecified the preceding “.” shall not be
     * included. The offset suffix, if present, shall contain 4 digits. The string may be padded
     * with trailing SPACE characters. Leading and embedded spaces are not allowed.</p>
     * <p>A component that is omitted from the string is termed a null component. Trailing null
     * components of Date Time indicate that the value is not precise to the precision of those
     * components. The YYYY component shall not be null. Non-trailing null components are
     * prohibited. The optional suffix is not considered as a component.</p>
     * <p>A Date Time value without the optional suffix is interpreted to be in the local time zone of the
     * application creating the Data Element, unless explicitly specified by the Timezone Offset
     * From UTC (0008,0201). UTC offsets are calculated as "local time
     * minus UTC". The offset for a Date Time value in UTC shall be +0000.</p>
     * <p/>
     * Notes:
     * <ul>
     * <li>The range of the offset is -1200 to +1400. The offset for United States
     * Eastern Standard Time is -0500. The offset for Japan Standard Time is
     * +0900.
     * </li>
     * <li>The RFC 2822 use of -0000 as an offset to indicate local time is not allowed.</li>
     * <li>A Date Time value of 195308 means August 1953, not specific to particular
     * day. A Date Time value of 19530827111300.0 means August 27, 1953, 11;13 a.m. accurate to 1/10th second.</li>
     * <li>The Second component may have a value of 60 only for a leap second.</li>
     * <li>The offset may be included regardless of null components; e.g., 2007-0500 is a legal value.</li>
     * </ul>
     */
    public static byte[] DT = {'D', 'T'};
    /**
     * Single precision binary floating point number
     * represented in IEEE 754:1985 32-bit Floating
     * Point Number Format.
     */
    public static byte[] FL = {'F', 'L'};
    /**
     * Double precision binary floating point number
     * represented in IEEE 754:1985 64-bit Floating
     * Point Number Format.
     */
    public static byte[] FD = {'F', 'D'};
    /**
     * A string of characters representing an Integer
     * in base-10 (decimal), shall contain only the
     * characters 0 - 9, with an optional leading "+" or
     * "-". It may be padded with leading and/or
     * trailing spaces. Embedded spaces are not
     * allowed.
     * <p/>
     * The integer, n, represented shall be in the
     * range:
     * -2^31 <= n <= (2^31 - 1).
     */
    public static byte[] IS = {'I', 'S'};
    /**
     * A character string that may be padded with
     * leading and/or trailing spaces. The character
     * code 5CH (the BACKSLASH “\” in ISO-IR 6)
     * shall not be present, as it is used as the
     * delimiter between values in multiple valued
     * data elements. The string shall not have
     * Control Characters except for ESC.
     */
    public static byte[] LO = {'L', 'O'};
    /**
     * A character string that may contain one or
     * more paragraphs. It may contain the Graphic
     * Character set and the Control Characters, CR,
     * LF, FF, and ESC. It may be padded with
     * trailing spaces, which may be ignored, but
     * leading spaces are considered to be
     * significant. Data Elements with this VR shall
     * not be multi-valued and therefore character
     * code 5CH (the BACKSLASH “\” in ISO-IR 6)
     * may be used.
     */
    public static byte[] LT = {'L', 'T'};
    public static byte[] OB = {'O', 'B'};
    public static byte[] OF = {'O', 'F'};
    public static byte[] OW = {'O', 'W'};

    public static byte[] PN = {'P', 'N'};
    /**
     * A character string encoded using a 5
     * component convention. The character code
     * 5CH (the BACKSLASH “\” in ISO-IR 6) shall
     * not be present, as it is used as the delimiter
     * between values in multiple valued data
     * elements. The string may be padded with
     * trailing spaces. For human use, the five
     * components in their order of occurrence are:
     * family name complex, given name complex,
     * middle name, name prefix, name suffix.
     * Note: HL7 prohibits leading spaces within a
     * component; DICOM allows leading and
     * trailing spaces and considers them
     * insignificant.
     * Any of the five components may be an empty
     * string. The component delimiter shall be the
     * caret “^” character (5EH). Delimiters are
     * required for interior null components. Trailing
     * null components and their delimiters may be
     * omitted. Multiple entries are permitted in each
     * component and are encoded as natural text
     * strings, in the format preferred by the named
     * person.
     * For veterinary use, the first two of the five
     * components in their order of occurrence are:
     * responsible party family name or responsible
     * organization name, patient name. The
     * remaining components are not used and shall
     * not be present.
     * This group of five components is referred to as
     * a Person Name component group.
     * For the purpose of writing names in
     * ideographic characters and in phonetic
     * characters, up to 3 groups of components (see
     * Annexes H, I and J) may be used. The
     * delimiter for component groups shall be the
     * equals character “=” (3DH). The three
     * component groups of components in their
     * order of occurrence are: an alphabetic
     * representation, an ideographic representation,
     * and a phonetic representation.
     * Any component group may be absent,
     * including the first component group. In this
     * case, the person name may start with one or
     * more “=” delimiters. Delimiters are required for
     * interior null component groups. Trailing null
     * component groups and their delimiters may be
     * omitted.
     * Precise semantics are defined for each
     * component group. See section 6.2.1.
     */

    public static byte[] SH = {'S', 'H'};
    /**
     * A character string that may be padded with
     * leading and/or trailing spaces. The character
     * code 05CH (the BACKSLASH “\” in ISO-IR 6)
     * shall not be present, as it is used as the
     * delimiter between values for multiple data
     * elements. The string shall not have Control
     * Characters except ESC.
     */
    public static byte[] SL = {'S', 'L'};
    /**
     * Signed binary integer 32 bits long in 2's
     * complement form.
     * Represents an integer, n, in the range:
     * - 231 <= n <= (231 - 1).
     */
    public static byte[] SQ = {'S', 'Q'};
    public static byte[] SS = {'S', 'S'};
    /**
     * Signed binary integer 16 bits long in 2's
     * complement form. Represents an integer n in
     * the range:
     * -215 <= n <= (215 - 1).
     */
    public static byte[] ST = {'S', 'T'};
    /**
     * A character string that may contain one or
     * more paragraphs. It may contain the Graphic
     * Character set and the Control Characters, CR,
     * LF, FF, and ESC. It may be padded with
     * trailing spaces, which may be ignored, but
     * leading spaces are considered to be
     * significant. Data Elements with this VR shall
     * not be multi-valued and therefore character
     * code 5CH (the BACKSLASH “\” in ISO-IR 6)
     * may be used.
     */
    public static byte[] TM = {'T', 'M'};
    /**
     * A string of characters of the format
     * HHMMSS.FFFFFF; where HH contains hours
     * (range "00" - "23"), MM contains minutes
     * (range "00" - "59"), SS contains seconds
     * (range "00" - "60"), and FFFFFF contains a
     * fractional part of a second as small as 1
     * millionth of a second (range “000000” -
     * “999999”). A 24-hour clock is used. Midnight
     * shall be represented by only “0000“ since
     * “2400“ would violate the hour range. The
     * string may be padded with trailing spaces.
     * Leading and embedded spaces are not
     * allowed.
     * One or more of the components MM, SS, or
     * FFFFFF may be unspecified as long as every
     * component to the right of an unspecified
     * component is also unspecified, which indicates
     * that the value is not precise to the precision of
     * those unspecified components.
     * The FFFFFF component, if present, shall
     * contain 1 to 6 digitis. If FFFFFF is unspecified
     * the preceding “.” shall not be included.
     */
    public static byte[] UI = {'U', 'I'};
    /**
     * A character string containing a UID that is
     * used to uniquely identify a wide variety of
     * items. The UID is a series of numeric
     * components separated by the period "."
     * character. If a Value Field containing one or
     * more UIDs is an odd number of bytes in
     * length, the Value Field shall be padded with a
     * single trailing NULL (00H) character to ensure
     * that the Value Field is an even number of
     * bytes in length. See Section 9 and Annex B
     * for a complete specification and examples.
     */
    public static byte[] UL = {'U', 'L'};
    /**
     * Unsigned binary integer 32 bits long.
     * Represents an integer n in the range:
     * 0 <= n < 232.
     */
    public static byte[] UN = {'U', 'N'};
    public static byte[] US = {'U', 'S'};
    public static byte[] UT = {'U', 'T'};

    public static byte[] OX = {'O', 'X'};
    public static byte[] XO = {'X', 'S'};

    public static byte[] NA = {'N', 'A'};
    public static byte[] UP = {'U', 'P'};
    private String code;
    private String name;

// -------------------------- STATIC METHODS --------------------------

    @SuppressWarnings({"ConstantConditions"})
    public static ValueRepresentation getVr(byte[] vr) throws Exception
    {
        if (vr == null)
            throw new Exception("Expected a 2 byte array for value representation, instead of a null parameter");
        if (vr.length != 2) throw new Exception("Expected a 2 byte array for value representation");

        if (vr[0] == 'A' && vr[1] == 'E') return ApplicationEntity;
        else if (vr[0] == 'A' && vr[1] == 'S') return AgeString;
        else if (vr[0] == 'A' && vr[1] == 'T') return AttributeTag;
        else if (vr[0] == 'C' && vr[1] == 'S') return CodeString;
        else if (vr[0] == 'D' && vr[1] == 'A') return Date;
        else if (vr[0] == 'D' && vr[1] == 'S') return DecimalString;
        else if (vr[0] == 'D' && vr[1] == 'T') return DateTime;

        else if (vr[0] == 'F' && vr[1] == 'L') return FloatingPointSingle;
        else if (vr[0] == 'F' && vr[1] == 'D') return FloatingPointDouble;
        else if (vr[0] == 'I' && vr[1] == 'S') return IntegerString;
        else if (vr[0] == 'L' && vr[1] == 'O') return LongString;
        else if (vr[0] == 'L' && vr[1] == 'T') return LongText;
        else if (vr[0] == 'O' && vr[1] == 'B') return OtherByteString;
        else if (vr[0] == 'O' && vr[1] == 'F') return OtherFloatString;
        else if (vr[0] == 'O' && vr[1] == 'W') return OtherWordString;

        else if (vr[0] == 'P' && vr[1] == 'N') return PersonName;
        else if (vr[0] == 'S' && vr[1] == 'H') return ShortString;
        else if (vr[0] == 'S' && vr[1] == 'L') return SignedLong;
        else if (vr[0] == 'S' && vr[1] == 'Q') return SequenceOfItems;
        else if (vr[0] == 'S' && vr[1] == 'S') return SignedShort;
        else if (vr[0] == 'S' && vr[1] == 'T') return ShortText;
        else if (vr[0] == 'T' && vr[1] == 'M') return Time;
        else if (vr[0] == 'U' && vr[1] == 'I') return UniqueIdentifier;

        else if (vr[0] == 'U' && vr[1] == 'L') return UnsignedLong;
        else if (vr[0] == 'U' && vr[1] == 'N') return Unknown;
        else if (vr[0] == 'U' && vr[1] == 'S') return UnsignedShort;
        else if (vr[0] == 'U' && vr[1] == 'T') return UnlimitedText;

        /**
         if(vr[0]=='O' && vr[1]== 'X') return OX;
         if(vr[0]=='X' && vr[1]== 'S') return XO;
         */

        else if (vr[0] == 'N' && vr[1] == 'A') return NotApplicable;
        else if (vr[0] == 'U' && vr[1] == 'P') return UnsignedPointer;
        else
            throw new Exception(
                    "Unknown value representation (" + Arrays.toString(vr) + ") based on the given byte array");
    }

// --------------------------- CONSTRUCTORS ---------------------------

    public ValueRepresentation(byte[] VR)
    {
        this(new String(VR), "");
    }

    public ValueRepresentation(String code, String name)
    {
        if (code == null)
        {
            throw new NullPointerException("Value representation code may not be null while initialising the object.");
        }
        if (code.equals(""))
        {
            throw new NullPointerException("Value representation code may not be empty while initialising the object.");
        }
        this.code = code;
        this.name = name;
    }

// --------------------- GETTER / SETTER METHODS ---------------------

    public String getCode()
    {
        return code;
    }

    public String getName()
    {
        return name;
    }

// ------------------------ CANONICAL METHODS ------------------------

    @Override
    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (!(o instanceof ValueRepresentation)) return false;

        ValueRepresentation that = (ValueRepresentation) o;

        //return code.equals(that.code) && name.equals(that.name);
        return code.equals(that.code);
    }

    @Override
    public int hashCode()
    {
        int result = code.hashCode();
        result = 31 * result + name.hashCode();
        return result;
    }

    public String toString()
    {
        return "[" + code + "] " + name;
    }

// ------------------------ INTERFACE METHODS ------------------------


// --------------------- Interface Comparable ---------------------

    public int compareTo(ValueRepresentation o)
    {
        return getCode().compareTo(o.getCode());
    }

/*DVR_NONE, DVR_AE, DVR_AS, DVR_AT, DVR_CS, DVR_DA, DVR_DS, DVR_DT,
  DVR_FL,   DVR_FD, DVR_IS, DVR_LO, DVR_LT, DVR_OB, DVR_OF, DVR_OW,
  DVR_PN,   DVR_SH, DVR_SL, DVR_SQ, DVR_SS, DVR_ST, DVR_TM, DVR_UI,
  DVR_UL,   DVR_UN, DVR_US, DVR_UT,
  DVR_ox,  // OB or OW depending on context
  DVR_xs,  // SS or US depending on context
  DVR_lt,  *//* US, SS or OW depending on context, used for LUT Data
                        (thus the name) *//*
            DVR_na,  // na="not applicable", for data which has no VR
            DVR_up   // up="unsigned pointer", used internally for DICOMDIR support*/
}
